package com.component.api.utils;

import com.alibaba.fastjson.JSON;
import com.component.api.constants.ComponentCodeConstants;
import com.component.api.excepiton.ComponentException;
import com.component.api.model.UnifiedType;
import com.google.common.graph.Graph;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.Graphs;
import com.google.common.graph.MutableGraph;
import com.component.api.AbstractComponent;
import com.component.api.model.PortData;
import com.component.api.model.data.ColumnData;
import com.component.api.model.flow.FlowInfo;
import com.component.api.model.flow.NodeInfo;
import com.component.api.model.flow.RelationInfo;
import com.component.api.model.flow.SortNodeInfo;
import com.component.api.model.param.ComponentDefine;
import com.component.api.model.param.ParamPair;
import com.component.api.model.param.Port;
import com.component.api.model.param.TableColumn;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 描述：
 * 流程工具类
 * @author xianggj
 * @Date 2021/12/2 11:19
 **/
public class FlowUtils {

    /**
     * 预处理节点
     * @param flowInfo
     * @param currentNodeId
     * @return
     */
    public static Map<String, List<TableColumn>> pretreatmentFields(FlowInfo flowInfo, String currentNodeId){
        Map<String, List<TableColumn>> map = new HashMap<>();
        List<RelationInfo> edges = flowInfo.getEdges();

        List<NodeInfo> nodes = flowInfo.getNodes();
        Map<String, NodeInfo> nodeMap = nodes.stream()
                .collect(Collectors.toMap(NodeInfo::getId, Function.identity()));
        NodeInfo currentNodeInfo = nodeMap.get(currentNodeId);
        Set<NodeInfo> starts = new HashSet<>();
        //图
        Graph<NodeInfo> graph = analysisGraph(nodes, edges, starts);
        //首节点解析 前面没有节点了
        if (starts.contains(currentNodeInfo) ){
            List<PortData> portData = getFirstTableColumns(currentNodeInfo);
            if (portData.size() == 0){
               throw new ComponentException(String.format("源节点【%s】无输出字段", currentNodeInfo.getName()));
            }
            //目前没有引进端口 这里取第一个
            List<TableColumn> columns = ((ColumnData) portData.get(0).getValue()).getColumns();
            map.put(currentNodeId, columns);
            return map;
        }

        Set<NodeInfo> nodeInfos = graph.predecessors(currentNodeInfo);
        for (NodeInfo nodeInfo : nodeInfos) {
            List<PortData> portData = new SortNodeInfo(nodeInfo, graph).getPortData();
            if (portData.size() == 0){
                throw new ComponentException(String.format("节点【%s】无输出字段", currentNodeInfo.getName()));
            }
            //目前没有引进端口 这里取第一个
            List<TableColumn> columns = ((ColumnData) portData.get(0).getValue()).getColumns();
            //结果过滤
            String rule = currentNodeInfo.getRule();
            columns = filter(columns, rule);
            map.put(nodeInfo.getId(), columns);
        }

        return map;
    }

    /**
     * 根据类型过滤
     * @param columns
     * @param rule
     * @return
     */
    private static List<TableColumn> filter(List<TableColumn> columns, String rule) {
        //只能是varchar
        if (ComponentCodeConstants.DICTIONARY_SPECIFICATION.equalsIgnoreCase(rule)){
            return columns.stream()
                    .filter(e-> UnifiedType.STRING.equals(TypesUtils.unifiedType(e.getDataType())))
                    .collect(Collectors.toList());
        }
        //只能是num
        if (ComponentCodeConstants.NUMERICAL_VALIDATION.equalsIgnoreCase(rule) ||
                ComponentCodeConstants.DATA_TO_A.equalsIgnoreCase(rule) ||
                ComponentCodeConstants.UNIFIED_OUTPUT.equalsIgnoreCase(rule) ||
                ComponentCodeConstants.DEFAULT_PADDING.equalsIgnoreCase(rule)
        ){
            return columns.stream()
                    .filter(e-> {
                        UnifiedType unifiedType = TypesUtils.unifiedType(e.getDataType());
                        return UnifiedType.INTEGER.equals(unifiedType) ||
                                UnifiedType.LONG.equals(unifiedType) ||
                                UnifiedType.DOUBLE.equals(unifiedType);
                    })
                    .collect(Collectors.toList());
        }
        //只能是num string datatime
        if (ComponentCodeConstants.OUTLIERS_REMOVE.equalsIgnoreCase(rule)){
            return columns.stream()
                    .filter(e-> {
                        UnifiedType unifiedType = TypesUtils.unifiedType(e.getDataType());
                        return !UnifiedType.OTHER.equals(unifiedType) ;
                    })
                    .collect(Collectors.toList());
        }
        return columns;

    }

    /**
     * 获取首节点的解析列
     * @param currentNodeInfo
     * @return
     */
    private static List<PortData> getFirstTableColumns(NodeInfo currentNodeInfo) {
        String rule = currentNodeInfo.getRule();
        //首节点 是否可以 首节点特点输入线为0
        ComponentDefine comdef = ComponentUtils.getComponentDefineByRule(rule);
        List<Port> ports = comdef.getPorts();
        //有输入 就不是首节点 放弃解析
        if (ports.stream().anyMatch(e->Port.IN.equals(e.getType()))){
                return new ArrayList<>();
        }
        List<PortData> portData = null;
        try {
            List<ParamPair> paramPairs = JSON.parseArray(currentNodeInfo.getParam(), ParamPair.class);
            AbstractComponent component = ComponentUtils.getComponentByRule(rule);
            portData = component.previewOutputColumns(new ArrayList<>(), paramPairs);
            portData.stream().forEach(e->e.setNodeId(currentNodeInfo.getId()));
        } catch (Exception e) {
            e.printStackTrace();
            //异常拼凑节点名称
            throw new ComponentException(String.format("节点【%s】字段解析出错：", currentNodeInfo.getName()) , e);
        }
        return  portData;

    }

    /**
     * 分析图
     * @param nodes
     * @param edges
     */
    public static Graph<NodeInfo> analysisGraph(List<NodeInfo> nodes,
                                          List<RelationInfo> edges, Set<NodeInfo> startNodes) {
        //必须是有向拓扑图 不允许自循环
        MutableGraph<NodeInfo> graph = GraphBuilder.directed().allowsSelfLoops(false).build();
        for (NodeInfo nodeInfo : nodes) {
            graph.addNode(nodeInfo);
        }
        Map<String, NodeInfo> nodeMap = nodes.stream()
                .collect(Collectors.toMap(NodeInfo::getId, Function.identity()));
        for (RelationInfo relationInfo : edges) {
            graph.putEdge(nodeMap.get(relationInfo.getSource()), nodeMap.get(relationInfo.getTarget()));
        }
        if (Graphs.hasCycle(graph)){
            throw new RuntimeException("这是个有循环图");
        }

        for (NodeInfo node : graph.nodes()) {
            //首节点
            Set<NodeInfo> predecessors = graph.predecessors(node);
            if (predecessors.isEmpty()){
                startNodes.add(node);
                node.setIsFirstNode(true);
            }
            //尾节点
            Set<NodeInfo> successors = graph.successors(node);
            if (successors.isEmpty()){
                node.setIsExec(true);
            }
        }
        return graph;
    }
}
